﻿using System.IO;
using GameFramework.Extensions;
using GameFramework.Managers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GameFramework.GameObjects
{
    /// <summary>
    /// Information related to the horizontal text alignment
    /// </summary>
    public enum HorizontalTextAlignment
    {
        /// <summary>
        /// The text is aligned to the left
        /// </summary>
        Left,

        /// <summary>
        /// The text is aligned to the center
        /// </summary>
        Center,

        /// <summary>
        /// The text is aligned to the right
        /// </summary>
        Right
    }

    /// <summary>
    /// Information related to the vertical text alignment
    /// </summary>
    public enum VerticalTextAlignment
    {
        /// <summary>
        /// The text is aligned to the top
        /// </summary>
        Top,

        /// <summary>
        /// The text is aligned to the center
        /// </summary>
        Center,

        /// <summary>
        /// The text is aligned to the bottom
        /// </summary>
        Bottom,
    }

    /// <summary>
    /// This game object represent a text.
    /// </summary>
    public class Label : Texture
    {
        /// <summary>
        /// The font.
        /// </summary>
        private SpriteFont textFont;

        /// <summary>
        /// The font name.
        /// </summary>
        private string fontName;

        /// <summary>
        /// The text.
        /// </summary>
        private string text;

        /// <summary>
        /// The size of the text.
        /// </summary>
        private Vector2 textSize;

        /// <summary>
        /// The horizontal text alignment.
        /// </summary>
        private HorizontalTextAlignment horizontalTextAlignment;

        /// <summary>
        /// The vertical text alignment.
        /// </summary>
        private VerticalTextAlignment verticalTextAlignment;

        /// <summary>
        /// Initializes a new instance of the <see cref="Label"/> class.
        /// </summary>
        public Label()
        {
            text = string.Empty;
            fontName = string.Empty;
            HorizontalTextAlignment = HorizontalTextAlignment.Center;
            VerticalTextAlignment = VerticalTextAlignment.Center;
            Color = Color.White;
        }

        /// <summary>
        /// Gets or sets the text.
        /// </summary>
        public string Text
        {
            get
            {
                return text;
            }

            set
            {
                if (text != value)
                {
                    text = value;
                    UpdateOrigin();
                }
            }
        }

        /// <summary>
        /// Gets or sets the font name.
        /// </summary>
        public string FontName
        {
            get
            {
                return fontName;
            }

            set 
            { 
                fontName = value ?? string.Empty;
                FontManager.Fonts.TryGetValue(fontName, out textFont);

                UpdateOrigin();
            }
        }

        /// <summary>
        /// Gets the label bounding box.
        /// </summary>
        public override Rectangle BoundingBox
        {
            get
            {
                return new Rectangle(
                    (int)(PositionX - OriginX * ScaleX), 
                    (int)(PositionY - OriginY * ScaleY), 
                    (int)(textSize.X), 
                    (int)(textSize.Y));
            }
        }

        /// <summary>
        /// Gets or sets the horizontal text alignment.
        /// </summary>
        public HorizontalTextAlignment HorizontalTextAlignment 
        { 
            get
            {
                return horizontalTextAlignment;
            }
            
            set 
            { 
                if (horizontalTextAlignment != value)
                {
                    horizontalTextAlignment = value;
                    UpdateOrigin();
                }
            }
        }

        /// <summary>
        /// Gets or sets the vertical text alignment.
        /// </summary>
        public VerticalTextAlignment VerticalTextAlignment
        {
            get
            {
                return verticalTextAlignment;
            }

            set
            {
                if (verticalTextAlignment != value)
                {
                    verticalTextAlignment = value;
                    UpdateOrigin();
                }
            }
        }

        /// <summary>
        /// Draw the label on the screen.
        /// </summary>
        public override void Draw()
        {
            if (!Visible || textFont == null || string.IsNullOrEmpty(text))
            {
                return;
            }

            SpriteBatch.DrawString(textFont, Text, Position, Color, Angle, Origin, Scale, SpriteEffects.None, LayerDepth);
        }

        /// <summary>
        /// Serialize the label state into the given stream.
        /// </summary>
        /// <param name="writer">The writer.</param>
        public override void Write(BinaryWriter writer)
        {
            base.Write(writer);

            writer.WriteString(FontName);
            writer.WriteInt((int)horizontalTextAlignment);
            writer.WriteInt((int)verticalTextAlignment);
            writer.WriteString(Text);
        }

        /// <summary>
        /// Deserialize the label state from the given stream.
        /// </summary>
        /// <param name="reader">The reader.</param>
        public override void Read(BinaryReader reader)
        {
            base.Read(reader);

            FontName = reader.ReadString();
            horizontalTextAlignment = (HorizontalTextAlignment)reader.ReadInt();
            verticalTextAlignment = (VerticalTextAlignment)reader.ReadInt();
            Text = reader.ReadString();
        }

        /// <summary>
        /// Update the text origin and the text size.
        /// </summary>
        protected void UpdateOrigin()
        {
            if (textFont == null || string.IsNullOrEmpty(text))
            {
                return;
            }

            textSize = textFont.MeasureString(text);

            var halfWidth = textSize.X / 2;
            var halfHeight = textSize.Y / 2;

            Origin = new Vector2(
                halfWidth * (int)HorizontalTextAlignment, 
                halfHeight * (int)VerticalTextAlignment);
        }
    }
}